package com.yifeng.repo.controller.excel.loader.redis;

import com.google.common.base.Strings;
import com.yifeng.repo.base.utils.converter.JacksonHelper;
import com.yifeng.repo.storage.redis.RedisCacheWorker;
import com.yifeng.repo.storage.redis.RedisLockWorker;
import com.yifeng.repo.controller.excel.configure.ExcelLoaderProperties;
import com.yifeng.repo.controller.excel.loader.dto.DataTaskDto;
import com.yifeng.repo.controller.excel.loader.dto.DataTaskTypeEnum;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by daibing on 2023/1/26.
 */
@Slf4j
public class ExcelLoaderRedisWorker {
    private final String applicationName;
    private final ExcelLoaderProperties properties;
    private final RedisCacheWorker redisCacheWorker;
    private final RedisLockWorker redisLockWorker;

    private static final int REDIS_TTL_SECONDS = 30 * 24 * 60 * 60;

    public ExcelLoaderRedisWorker(String applicationName, ExcelLoaderProperties properties, RedisCacheWorker redisCacheWorker, RedisLockWorker redisLockWorker) {
        this.applicationName = applicationName;
        this.properties = properties;
        this.redisCacheWorker = redisCacheWorker;
        this.redisLockWorker = redisLockWorker;
    }

    /**
     * redis空间规划：
     * 1、字符串：数据任务全局唯一锁，key是requestKey，value是OWNER_ID
     * 2、HashMap：每一个应用对应一个HashMap，key是applicationName，field是requestKey+导入&导出类型，value是DataTaskDto对象json字符串
     */

    public boolean lock(String requestKey, String lockerId, long expireMillis) {
        String keyByLock = getKeyByLock(requestKey);
        return redisLockWorker.lock(keyByLock, lockerId, expireMillis);
    }

    public String saveDataTask(DataTaskDto dataTaskDto) {
        String keyByApp = this.getKeyByApp();
        String fieldByTask = this.getFieldByTask(dataTaskDto.getRequestKey(), dataTaskDto.getType());
        String json = JacksonHelper.toJson(dataTaskDto);
        redisCacheWorker.hashPut(keyByApp, REDIS_TTL_SECONDS, fieldByTask, json);
        return String.format("key=%s;field=%s;value=%s", keyByApp, fieldByTask, json);
    }

    public DataTaskDto getDataTask(String requestKey, DataTaskTypeEnum type) {
        String keyByApp = this.getKeyByApp();
        String fieldByTask = this.getFieldByTask(requestKey, type);
        String dataTask = redisCacheWorker.hashGet(keyByApp, fieldByTask);
        if (Strings.isNullOrEmpty(dataTask)) {
            return null;
        }
        return JacksonHelper.toObj(dataTask, DataTaskDto.class);
    }

    public List<DataTaskDto> listDataTask() {
        String keyByApp = this.getKeyByApp();
        List<String> dataTaskList = redisCacheWorker.hashValueSet(keyByApp);
        if (dataTaskList.isEmpty()) {
            return Collections.emptyList();
        }
        return dataTaskList.stream()
                .map(s -> JacksonHelper.toObj(s, DataTaskDto.class))
                .sorted(Comparator.comparing(DataTaskDto::getCreateTime).reversed())
                .collect(Collectors.toList());
    }

    public int runningDataTaskSize() {
        String keyByApp = this.getKeyByApp();
        List<String> dataTaskList = redisCacheWorker.hashValueSet(keyByApp);
        if (dataTaskList.isEmpty()) {
            return 0;
        }
        int size = 0;
        for (String dataTask : dataTaskList) {
            DataTaskDto dataTaskDto = JacksonHelper.toObj(dataTask, DataTaskDto.class);
            // 任务未完成，并且在刷新时间周期内，就认为正在运行的任务
            if (!dataTaskDto.isTotalFinished() && dataTaskDto.getRefreshTime().getTime() + properties.getLiveProbeIntervalMillis() >= System.currentTimeMillis()) {
                size++;
            }
        }
        return size;
    }

    public List<DataTaskDto> clearDataTask(int prevDays) {
        String keyByApp = this.getKeyByApp();
        Map<String, String> map = redisCacheWorker.hashGet(keyByApp);
        List<String> clearFields = new ArrayList<>();
        List<DataTaskDto> clearDataTasks = new ArrayList<>();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            DataTaskDto dataTaskDto = JacksonHelper.toObj(entry.getValue(), DataTaskDto.class);
            if (dataTaskDto.getCreateTime().getTime() + prevDays * 24 * 60 * 60 * 1000L > System.currentTimeMillis()) {
                continue;
            }
            clearFields.add(entry.getKey());
            clearDataTasks.add(dataTaskDto);
        }
        if (clearFields.isEmpty()) {
            return Collections.emptyList();
        }
        int clear = redisCacheWorker.hashRemoveField(keyByApp, clearFields.toArray(new String[0])).intValue();
        return clear == 0 ? Collections.emptyList() : clearDataTasks;
    }

    /**
     * 获取数据任务全局唯一锁的key
     */
    private String getKeyByLock(String requestKey) {
        return properties.getRedisKeyPrefix().concat(applicationName).concat(":").concat(requestKey);
    }

    /**
     * 获取应用HashMap对应的key
     */
    private String getKeyByApp() {
        return properties.getRedisKeyPrefix().concat(applicationName);
    }

    /**
     * 获取应用HashMap对应的field
     */
    private String getFieldByTask(String requestKey, DataTaskTypeEnum type) {
        return properties.getRedisKeyPrefix().concat(applicationName).concat(":").concat(type.name().toLowerCase()).concat(":").concat(requestKey);
    }

}
